/* 
 * File:   MixEditor.cpp
 * Author: ivosh-l
 * 
 * Created on 9. leden 2012, 23:08
 */

#include "MixEditor.h"
#include "CBlowfish.h"
#include "mix_dexoder.h"
#include "mix_file.h"
#include <fstream>
#include <cstring>
#include <vector>
#include <string>

using namespace std;

char MixEditor::addFile(std::string file) {
    t_mix_index_entry newfile;
    vector<t_mix_index_entry> new_index = files;
    t_mix_header new_head = mix_head;
    unsigned long begin, end;
    unsigned int size;
    ifstream ifh;
    char * buff;

    if (checkFileName(file))
        return 0;

    ifh.open(file.c_str(), ios::binary);
    /* get file size */
    begin = ifh.tellg();
    ifh.seekg(0, ios::end);
    end = ifh.tellg();
    size = (unsigned int) (end - begin);

    buff = new char[size + new_head.size];

    fh.seekg(dataoffset);
    fh.read(buff, new_head.size);

    ifh.seekg(0);
    ifh.read(buff + new_head.size, size);
    ifh.close();

    /* update file header */
    newfile.offset = new_head.size;
    newfile.size = size;
    newfile.id = getID(game_ts, file);
    new_index.push_back(newfile);

    /* update mix index */
    new_head.c_files++;
    new_head.size += size;

    /* write header and entry */
    if (m_is_encrypted) {
        saveEncryptedFile("modified.mix", new_head, new_index, buff, key_source);
    } else {
        saveFile("modified.mix", new_head, new_index, buff);
    }

    delete[] buff;
    return 'a';
}

bool MixEditor::removeID(int fileID) {
    int i;
    char * buff;
    bool found = false;
    vector<t_mix_index_entry> new_index = files;
    t_mix_header new_head = mix_head;

    for (i = 0; i < new_head.c_files; i++) {
        if (new_index[i].id == fileID) {
            found = true;
            break;
        }
    }
    if (!found)
        return false;

    /* read body without removed file */
    buff = new char[new_head.size - new_index[i].size];
    fh.seekg(dataoffset);
    fh.read(buff, new_index[i].offset);

    fh.seekg(dataoffset + new_index[i].offset + new_index[i].size);
    fh.read(buff + new_index[i].offset, new_head.size - new_index[i].offset - new_index[i].size);

    /* edit files offset */
    for (int j = 0; j < new_head.c_files; j++) {
        if (j == i)
            continue;
        if (new_index[j].offset > new_index[i].offset)
            new_index[j].offset -= new_index[i].size;
    }

    /* change header */
    new_head.size -= new_index[i].size;
    new_head.c_files--;

    /* edit index */
    new_index.erase(new_index.begin() + i);

    /* write header and entry */
    if (m_is_encrypted) {
        saveEncryptedFile("modified.mix", new_head, new_index, buff, key_source);
    } else {
        saveFile("modified.mix", new_head, new_index, buff);
    }

    delete[] buff;
}

bool MixEditor::saveEncryptedFile(std::string outFile, t_mix_header header, vector< t_mix_index_entry > mix_index, char * body, char * keySource) {
    ofstream ofh;
    Cblowfish blfish;
    int indexSize = 6;
    int blockCnt;
    char newKey[56];
    char * decIndex;
    
    ofh.open(outFile.c_str(), ios::binary);

    if (!keySource)
        keySource = key_source;

    get_blowfish_key((byte *) keySource, (byte *) newKey);

    blfish.set_key((byte *) newKey, 56);

    /* write flags (is encrypted, has no checksum) */
    ofh.write((char *) &mix_encrypted, sizeof (mix_encrypted));
    ofh.write(keySource, 80);

    /* copy header and index to buffer */
    decIndex = new char[6 + header.c_files * 12 + 8];

    memcpy(decIndex, (char *) &(header.c_files), 2);
    memcpy(decIndex + 2, (char *) &(header.size), 4);

    for (int i = 0; i < header.c_files; i++) {
        memcpy(decIndex + 6 + i * 12, (char *) &(mix_index[i]), 12);
        indexSize += 12;
    }

    blockCnt = indexSize / 8;
    if (indexSize % 8)
        blockCnt++;

    /* encrypt buffer in 8B blocks and write to file */
    for (int i = 0; i < blockCnt; i++) {
        memcpy(decrypt_buffer, decIndex + i * 8, 8);
        blfish.encipher((void *) decrypt_buffer, (void *) decrypt_buffer, 8);
        ofh.write(decrypt_buffer, 8);
    }

    ofh.write(body, header.size);
    ofh.close();

    return true;
}

bool MixEditor::saveFile(std::string outFile, t_mix_header header, vector< t_mix_index_entry > mix_index, char * body) {
    ofstream ofh;
    ofh.open(outFile.c_str(), ios::binary);

    /* write head and index */
    ofh.write((char *) &(header.c_files), 2);
    ofh.write((char *) &(header.size), 4);
    for (int i = 0; i < header.c_files; i++) {
        ofh.write((char *) &(mix_index[i]), 12);
    }

    /* write body */
    ofh.write(body, header.size);
    ofh.close();

    return true;
}

bool MixEditor::removeFile(std::string fileName) {
    return removeID(this->getID(game_ts, fileName));
}

char * MixEditor::generateLocalMixDatabase(const vector<string> fnames) {
    char * ret, * p;
    p = ret;
    const string crtinfo = "Created with TSMix 0.3.x.";
    unsigned int rsize = crtinfo.length();
    if (!fnames.size()) {
        readFileNames();
        //fnames = filenames;
    }
    for (unsigned int i = 0; i < fnames.size(); i++) {
        rsize += (fnames[i].length() + 1);

    }

    ret = new char[rsize];

    memcpy(p, crtinfo.c_str(), crtinfo.length());
    p += crtinfo.length();
    p[1] = '\0';
    p++;

    for (unsigned int i = 0; i < fnames.size(); i++) {
        memcpy(p, fnames[i].c_str(), fnames[i].length());
        p += (fnames[i].length());
        p[1] = '\0';
        p++;
    }

    return ret;
}